#include<unistd.h>
#include<sys/socket.h>
#include<sys/types.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<fcntl.h>
#include<sys/stat.h>

#include<mysql/mysql.h>

#include "queue.h"
#include "threadpool.h"
#include "cJSON.h

//#define LOCALLHOST 127.0.0.1

//生产者线程
void *producer(void *arg);
//消费者线程
void *consumer(void *arg);
//用户注册
int user_register(const char *username, const char *password);
//用户登录
int user_login(const char *username, const char *password);
//用户修改密码
int user_change_password(const char *username, const char *password);

int main(int argc, const char* argv[]){
	pthread_t tid1 = 0;
	int retval = 0;
	struct threadPool *pool = NULL;
	
	//调用线程池函数
	pool = createThreadPool(10, 100, consumer);

	//创建一个生产者线程
	retval = pthread_create(&tid1, NULL, producer, pool);
	//成功：返回0，失败：返回非0值
	if(retval != 0)
	{
		perror("pthread_create");  //线程创建失败
		exit(-1);
	}

	//回收线程资源
	//函数原型：int pthread_join(pthread_t thread, void **retval)
	//第1个参数为被等待的线程 ID
	//第2个参数为一个用户定义的指针,指向一个保存等待线程的完整退出状态的静态区域它可以用来存储被等待线程的返回值。
	pthread_join(tid1, NULL);

	//调用销毁线程池函数
	destroyThreadPool(pool, 100);
	
	return 0;
}

//生产者线程
void *producer(void *arg)
{
	struct threadPool *pool = (struct threadPool *)arg; //类型强制转换
	int listensock = 0;

	//功能：创建套接字
	//1.协议族 2.套接字类型(流式套接字) 3.指定应用程序所使用的通信协议
	listensock = socket(AF_INET, SOCK_STREAM, 0);
	//成功：返回新创建的套接字描述符，失败返回-1
	if(listensock == -1)
	{
		perror("socket");  //套接字创建失败 
		exit(-1);
	}

	struct sockaddr_in seraddr;
	memset(&seraddr, 0, sizeof(struct sockaddr));

	seraddr.sin_family = AF_INET;   //协议族
	seraddr.sin_port = htons(6868);  //端口
	seraddr.sin_addr.s_addr = inet_addr("127.0.0.1");  //ip地址
	
	int retval = 0;
	//功能：用来将指定的socket与对应的网络地址进行绑定
	//1.sockfd: 标识一个已链接接口的描述符
	//2.addr: 是一个指向sockaddr结构的指针，标识绑定本地地址信息
	//3.addrlen: 欲绑定的网络地址sockaddr的结构长度
	retval = bind(listensock, (struct sockaddr *)&seraddr, sizeof(struct sockaddr));
	//成功：返回0，失败：返回-1
	if(retval == -1)
	{
		perror("bind");    //绑定失败
		close(listensock); //关闭监听套接字
		exit(-1);
	}
	//功能：socket()函数创建的socket默认是一个主动类型的，listen函数将socket变为被动类型的，等待客户的链接请求
	//1.sockfd: 要监听的socket描述符
	//2.backlog: 相应socket可以排队的最大连接个数
	retval = listen(listensock, 5);
	//成功：返回0，失败：返回-1
	if(retval == -1)
	{
		perror("listen"); 
		close(listensock);  //关闭监听套接字
		exit(-1);
	}

	int fd = 0;
	while(1)
	{
		//功能：TCP服务器接受客户端的连接请求
		//1.socket: 服务器的socket描述符
		//2.addr: 指向struct sockaddr *的指针，用于返回客户端的协议地址
		//3.addrlen: socket地址的长度
		fd = accept(listensock, NULL, NULL);
		//成功：返回值是由内核自动生成的一个全新的描述符，代表返回与客户的TCP连接
		//失败：返回-1
		if(fd == -1)
		{
			perror("accept");  //TCP服务器接受客户端的连接请求失败
			close(listensock); //关闭监听套接字
			exit(-1);
		}
								
								 //入队
								 
		//p操作
		//阻塞函数。从信号量的值减去一个“1”，但它永远会先等待该信号量为一个非零值才开始做减法
		//函数原型：int sem_wait(sem_t *sem);
		sem_wait(&pool -> semw);
		//加锁
		//函数原型：int pthread_mutex_lock(pthread_mutex_t *mutex);
		//函数参数：1.mutex：被初始化的互斥锁
		//成功：返回 0；失败：返回错误号
		pthread_mutex_lock(&pool -> mutex);
		//尾插
		push(pool -> que, fd);
		//解锁
		pthread_mutex_unlock(&pool -> mutex);
		//v操作
		//函数功能: 给信号量的值加上一个“1”。
		//函数原型：int sem_post(sem_t *sem);
		//函数参数：1.sem：指向信号量结构的一个指针。
		//函数返回：成功：返回 0；失败：返回-1
		sem_post(&pool -> semr);
		
	}
	//关闭监听套接字
	close(listensock);
}

//消费者线程
void *consumer(void *arg)
{
	struct threadPool *pool = (struct threadPool *)arg;  //类型强制转换
	int fd = 0;
	int retval = 0;
	char buf[100] = {0};
	char type[20] = {0};
	char username[20] = {0};
	char password[20] = {0};
	char result[50] = {0};
	char *dest = NULL;

	while(1)
	{
							//出队
		//p操作
		sem_wait(&pool -> semr);
		//加锁
		pthread_mutex_lock(&pool -> mutex);
		//输出
		fd = front(pool -> que);
		//头删
		pop(pool -> que);
		//解锁
		pthread_mutex_unlock(&pool -> mutex);
		//v操作
		sem_post(&pool -> semw);

		while(1)
		{
			//功能：从一个已连接的socket接收数据
			//1.fd: 是一个已连接套接字的描述符
			//2.buf: 指定的缓冲区，用于存放收到的数据
			//3.len: 接收数据的长度
			//4.flags: 调用执行方式，一般为0
			retval = recv(fd, buf, sizeof(buf) - 1, 0);
			//成功：返回所接收数据的总数，失败：返回-1
			if(retval == -1)
			{
				perror("recv");  //接收失败
				close(fd);   //关闭通信套接字
				exit(-1);
			}
			if(retval == 0)
			{
				close(fd);
				break;
			}

								//解包
								
			//1.定义json类型指针
			cJSON *json = NULL;
			//2.由json格式字符串产生一个json对象
			json = cJSON_Parse(buf);
			//3.由键获取值
			cJSON *node = NULL;
			node = cJSON_GetObjectItem(json, "type");
			if(node == NULL)
			{
				printf("can't find\n");
				exit(-1);
			}
			if(node -> type == cJSON_String)
			{
				strcpy(type, node -> valuestring);
			}

			node = cJSON_GetObjectItem(json, "username");
			if(node == NULL)
			{
				printf("can't find\n");
				exit(-1);
			}
			if(node -> type == cJSON_String)
			{
				strcpy(username, node -> valuestring);
			}

			node = cJSON_GetObjectItem(json, "password");
			if(node == NULL)
			{
				printf("can't find\n");
				exit(-1);
			}
			if(node -> type == cJSON_String)
			{
				strcpy(password, node -> valuestring);
			}

								//处理
								
			if(strcmp(type, "register") == 0)  //判断是不是注册
			{
				//调用用户注册函数
				retval = user_register(username, password);
				if(retval == 0)
				{
					strcpy(result, "注册成功！");
				}
				else
				{
					strcpy(result, "注册失败！");
				}
			}
			else if(strcmp(type, "login") == 0)  //判断是不是登录
			{
				//调用用户登录函数
				retval = user_login(username, password);
			//	printf("%s, %s\n", username, password);
				if(retval == 0)
				{
					strcpy(result, "登录成功！");
				}
				else
				{
					strcpy(result, "登录失败！");
				}
			}
			else if(strcmp(type, "change") == 0)
			{
				//调用用户修改密码函数
				retval = user_change_password(username, password);
				if(retval == 0)
				{
					strcpy(result, "密码修改成功！");
				}
				else
				{
					strcpy(result, "密码修改失败！");
				}
			}

								//组包
								
			//定义json类型指针并初始化
			json = cJSON_CreateObject();
			//给json对象中增加"键-值"对
										//键		//将值转化为json格式类型
			cJSON_AddItemToObject(json, "result", cJSON_CreateString(result));
			//将json对象转化为标准的c格式字符串
			dest = cJSON_Print(json);

			retval = send(fd, dest, strlen(dest), 0);

			if(retval == -1)
			{
				perror("send");  //发送失败
				close(fd);   //关闭通信套接字
				exit(-1);
			}	

		}
	}
}

/*
函数功能：用户注册
函数参数：username: 用户名  password：密码
函数返回：成功返回0，失败返回-1
*/
int user_register(const char *username, const char *password)
{
	int retval = 0;
	
	//step1  mysql连接句柄(handler)初始化
	
	MYSQL *connection = NULL;
	//函数原型：MYSQL *mysql_init(MYSQL *mysql)
	connection = mysql_init(connection);
	//函数返回值： 成功返回一个被始化的MYSQL*句柄，失败：返回NULL（备注：在内存不足的情况下，返回NULL）
	if(connection == NULL)
	{
		printf("query: %s\n", mysql_error(connection));  
		retval = -1;
	//	mysql_close(connection);  //关闭连接，释放资源
	}
	
	//step2  建立连接
/*	
	函数功能：连接一个MySQL服务器
	函数原型: MYSQL *mysql_real_connect(MYSQL *mysql, const char *host, const char *user, const char *passwd, const char *db, unsigned int port, const char *unix_socket, unsigned int client_flag);
	函数传入值：1.mysql表示一个现存mysql结构的地址  2.host表示MYSQL服务器的主机名或IP  3.user表示登录的用户名  4.passwd表示登录的密码  5.db表示要连接的数据库    6.port表示MySQL服务器的TCP/IP端口  7.unix_socket表示连接类型 8.client_flag表示MySQL运行ODBC数据库的标记
*/
	connection = mysql_real_connect(connection, "127.0.0.1", "root", "Lbq12345!", "project", 3306, NULL, 0);
	//函数返回值： 如果连接成功，返回一个MYSQL*连接句柄：如果连接失败，返回NULL
	if(connection == NULL)
	{
		printf("query: %s\n", mysql_error(connection));
		retval = -1;
	//	mysql_close(connection);  //关闭连接，释放资源
	}
	
	mysql_query(connection, "SET NAMES utf8");  //设置中文字符集为utf8
		
	//step3  执行SQL语句
	
	char sql[128] = {0};
	//sprintf(sql, "insert into user_info values('%s', '%s');", user, passwd);
							  //表名       字段1        字段2              
	sprintf(sql, "insert into user_info(user_name, user_password) value('%s', '%s');", username, password);  //值1  值2
	//printf("sql: %s\n", sql); // 测试SQL语句是不是正常(函数可能执行成功，但不一定SQL语句正常)
	retval = mysql_real_query(connection, sql, strlen(sql));
	//函数返回值： 如果查询成功，为零，出错为非零。
	if(retval == -1)
	{
		printf("query: %s\n", mysql_error(connection));
		retval = -1;
	//	mysql_close(connection);  //关闭连接，释放资源
	}
	
/*
	函数功能： 关闭一个服务器连接，并释放与连接相关的内存
	函数原型： void mysql_close(MYSQL *mysql);
	函数传入值： MYSQL:类型的指针
	函数返回值： 无
*/	
	mysql_close(connection);  //关闭连接，释放资源
	
	return retval;
	
}

/*
函数功能：用户登录
函数参数：username: 用户名  password：密码
函数返回：成功返回0，失败返回-1
*/
int user_login(const char *username, const char *password)
{
	int retval = -1;
	printf("-----------\n");
	//step1  mysql连接句柄(handler)初始化
	MYSQL *connection = NULL;
	
	connection = mysql_init(connection);
	
	if(connection == NULL)
	{
		printf("query1: %s\n", mysql_error(connection));
		
	}
	printf("-----------\n");
	//step2  建立连接
/*	
	函数功能：连接一个MySQL服务器
	函数原型: MYSQL *mysql_real_connect(MYSQL *mysql, const char *host, const char *user, const char *passwd, const char *db, unsigned int port, const char *unix_socket, unsigned int client_flag);
	函数传入值：1.mysql表示一个现存mysql结构的地址  2.host表示MYSQL服务器的主机名或IP  3.user表示登录的用户名  4.passwd表示登录的密码  5.db表示要连接的数据库    6.port表示MySQL服务器的TCP/IP端口  7.unix_socket表示连接类型 8.client_flag表示MySQL运行ODBC数据库的标记
*/
	connection = mysql_real_connect(connection, "127.0.0.1", "root", "Lbq12345!", "project", 3306, NULL, 0);
	//函数返回值： 如果连接成功，返回一个MYSQL*连接句柄：如果连接失败，返回NULL
	if(connection == NULL)
	{
		printf("query2: %s\n", mysql_error(connection));
		mysql_close(connection);  //关闭连接，释放资源
		return -1;
	}
	printf("-----------\n");
	printf("conn: %p\n", connection);
	
	mysql_query(connection, "SET NAMES utf8");  //设置中文字符集为utf8 
	
	//step3  执行SQL语句
	
	char sql[128] = {0};
						    //字段名         //表名     //条件查询
	sprintf(sql, "select user_password from user_info where user_name = '%s';", username);
	
	//printf("sql: %s\n", sql); // 测试SQL语句是不是正常(函数可能执行成功，但不一定SQL语句正常)
	int ret = 0;
	ret = mysql_real_query(connection, sql, strlen(sql));
	//函数返回值： 如果查询成功，为零，出错为非零。
	if(ret != 0)
	{
		printf("query3: %s\n", mysql_error(connection));
		mysql_close(connection);  //关闭连接，释放资源
		return -1;
	}
	printf("-----------\n");
	MYSQL_RES *res;  //查询结果集，结构类型
	MYSQL_ROW row;	//存放一行查询结果的字符串数组
	
/*	
	函数功能： 为无缓冲的结果集获得结果标识符
	函数原型： MYSQL_RES *mysql_use_result(MYSQL *mysql);
	函数传入值： MYSQL:类型的指针
	函数返回值： 成功：返回一个MYSQL_RES结果结构，失败：返回NULL	
*/	
	res = mysql_use_result(connection);   //存储结果
	//函数返回值： 成功：返回一个MYSQL_RES结果结构，失败：返回NULL
	if(res == NULL)
	{
		printf("mysql_use_result error");
		mysql_close(connection);  //关闭连接，释放资源
		return -1;
	}
		printf("-----------\n");
	//函数功能：检索一个结果集合的下一行
	//函数原型：MYSQL_ROW mysql_fetch_row(MYSQL_RES *result);
	//函数参数：MYSQL_RES:结构的指针
	//函数返回：下一行的一个MYSQL_ROW结构。如果没有更多的行可检索或如果出现一个错误，NULL
	while((row = mysql_fetch_row(res)) != NULL)     //控制行
	{
		//函数功能：返回指定结果集中列的数量
		//函数原型：unsigned int mysql_num_fields(MYSQL_RES *res);
		//函数参数：MYSQL_RES 结构的指针
		//函数返回：结果集合中字段数量的一个无符号整数
		int j = 0;
		for( j = 0; j < mysql_num_fields(res); j++)  //控制列
		{
			printf("%s", row[j]);
			if(strcmp(row[j], password) == 0)
			{
				retval = 0;
			}
			
		}
	/*	if(j == mysql_num_fields(res))
		{
			retval = -1;
		} */
		printf("\n");
		printf("-----------\n");
	}
	
	mysql_free_result(res);  // 释放上面存放结果申请的空间
	
	mysql_close(connection);  //关闭连接，释放资源
	
	return retval;
}

/*
函数功能：用户修改密码
函数参数：username: 用户名  password：密码
函数返回：成功返回0，失败返回-1
*/
	
int user_change_password(const char *username, const char *password)
{
	int retval = 0;
	
	//step1  mysql连接句柄(handler)初始化		
	MYSQL *connection = NULL;
	connection = mysql_init(connection);
	
	if(connection == NULL)
	{
		printf("query1: %s\n", mysql_error(connection));
		retval = -1;
	}
	
	//step2  建立连接
	connection = mysql_real_connect(connection, "127.0.0.1", "root", "Lbq12345!", "project", 3306, NULL, 0);
	//函数返回值： 如果连接成功，返回一个MYSQL*连接句柄：如果连接失败，返回NULL
	if(connection == NULL)
	{
		printf("query2: %s\n", mysql_error(connection));
		retval = -1;
		mysql_close(connection);
	}
	
	mysql_query(connection, "SET NAMES utf8");   //设置中文字符集为utf8
	
	//step3  执行SQL语句
	
	char sql[128] = {0};
	//修改表中的数据
						  //表名         //修改的字段名                 //条件查询
	sprintf(sql, "update user_info set user_password = '%s' where user_name = '%s';", password, username);
	
	//printf("sql: %s\n", sql);  //测试SQL语句是不是正常(函数可能执行成功，但不一定SQL语句正常)
	
	int ret = 0;
	ret = mysql_real_query(connection, sql, strlen(sql));
	//函数返回值： 如果查询成功，为零，出错为非零。
	if(ret != 0)
	{
		printf("query3: %s\n", mysql_error(connection));
		mysql_close(connection);
		retval = -1;
	}
	
	mysql_close(connection);  //关闭连接，释放资源
	
	return retval;
}
	
	
	
	